using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using LC;

namespace Scan
{
    #region PowerNotify Event
    /// <summary>
    /// PowerNotifyEventArgs
    /// </summary>
    public class PowerNotifyEventArgs : EventArgs
    {
        private uint acLineStatus;

        private uint batteryLifePercent;

        public uint ACLineStatus
        {
            get { return acLineStatus; }
        }

        public uint BatteryLifePercent
        {
            get { return batteryLifePercent; }
        }

        public PowerNotifyEventArgs(uint acLineStatus, uint batteryLifePercent)
        {
            this.acLineStatus = acLineStatus;
            this.batteryLifePercent = batteryLifePercent;
        }
    }

    /// <summary>
    ///  PowerNotifyEvent Delegate
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public delegate void PowerNotifyEventHandler(object sender, PowerNotifyEventArgs e);
    #endregion
    
    public class PowerManager
    {
        private static NLog.Logger logger = NLog.LogManager.GetLogger("Power");


        #region Event
        public event PowerNotifyEventHandler PowerNotify = null;

        protected virtual void OnPowerNotify(PowerNotifyEventArgs e)
        {
            if (PowerNotify != null)
            {
                PowerNotify(this, e);
            }
        }
        #endregion

        #region Variable
        IntPtr hMsgQ;

        IntPtr[] hEvent = new IntPtr[4];
        #endregion
       
        #region Structure

        public PowerManager()
        {
            hEvent[0] = Win32.CreateEvent(IntPtr.Zero, false, false, null);
            Device.CreatePowerEvent(out hEvent[1], out hEvent[2]);
        }

        ~PowerManager()
        {
            for (int i = 0; i < hEvent.Length; ++i)
            {
                Win32.CloseHandle(hEvent[i]);
            }
        }

        #endregion

        #region Method
        /// <summary>
        /// Start  PowerManagement Thread
        /// </summary>
        /// <returns></returns>
        public bool Start()
        {
            logger.Info("Start Power Thread");
            Win32.MSGQUEUEOPTIONS options = new Win32.MSGQUEUEOPTIONS();
            options.dwSize = 20;
            options.dwFlags = 2;
            options.dwMaxMessages = 1;
            options.cbMaxMessage = 64;
            options.bReadAccess = true;

            hMsgQ = Win32.CreateMsgQueue(null, options);
            if (hMsgQ == IntPtr.Zero)
            {
                return false;
            }

            IntPtr hNotifi = Win32.RequestPowerNotifications(hMsgQ, Win32.PBT_POWERINFOCHANGE);
            if (hNotifi == IntPtr.Zero)
            {
                return false;
            }

            hEvent[3] = hMsgQ;

            Thread t = new Thread(new ThreadStart(this.PowerThreadPorc));
            t.Start();

            return true;
        }

        /// <summary>
        /// Stop PowerManagement Thread
        /// </summary>
        public void Stop()
        {
            logger.Info("Stop Power Thread");
            Win32.EventModify(hEvent[0], Win32.EVENT_SET);
            Win32.StopPowerNotifications(hMsgQ);
            Win32.CloseMsgQueue(hMsgQ);
        }

        /// <summary>
        /// PowerThreadPorc
        /// </summary>
        private void PowerThreadPorc()
        {
            Device.SuspendHold(true);

            while (true)
            {
                uint evt = Win32.WaitForMultipleObjects(4, hEvent, false, Win32.INFINITE);
                logger.Debug("PowerThreadPorc {0:D}", evt);
                switch (evt)
                {
                    case 0://return thread
                        Device.SuspendHold(false);
                        logger.Debug("Stop Power Thread");
                        return;
                    case 1://disable network 
                        logger.Info("Suspend Hold");
                        StopNetWork(false);
                        logger.Info("Continue Suspend");
                        Device.SuspendHold(false);
                        break;
                    case 2:
                        Device.SuspendHold(true);
                        logger.Info("Power resume");
                        Win32.SYSTEM_POWER_STATUS_EX2 status = new Win32.SYSTEM_POWER_STATUS_EX2();
                        if (Win32.GetSystemPowerStatusEx2(status, (uint)Marshal.SizeOf(typeof(Win32.SYSTEM_POWER_STATUS_EX2)), true) > 0)
                        {
                            logger.Info("BatteryLife{0:D}", status.BatteryLifePercent);
                            if (status.ACLineStatus != 0x01 && status.BatteryLifePercent <= 7)
                            {
                                StopNetWork(true);
                                logger.Info("force suspend");
                                Win32.SetSystemPowerState(null, Win32.POWER_STATE_SUSPEND, Win32.POWER_FORCE);
                                break;
                            }
                        }
                        Device.CheckNetworkStat();
                        Device.Com2PowerOn();
                        Thread.Sleep(300);
                        break;
                    case 3:
                        uint bytesRead;
                        uint flags;
                        Win32.POWER_BROADCAST pB = new Win32.POWER_BROADCAST();
                        if (Win32.ReadMsgQueue(hMsgQ, out pB, (uint)Marshal.SizeOf(typeof(Win32.POWER_BROADCAST)), out bytesRead, Win32.INFINITE, out flags))
                        {
                            logger.Info("BatteryLife{0:D} ACState {1:D}", pB.PI.bBatteryLifePercent, pB.PI.bACLineStatus);
                            if (pB.Message == Win32.PBT_POWERINFOCHANGE && pB.PI.bACLineStatus != 0x01 && pB.PI.bBatteryLifePercent <= 5)
                            {
                                StopNetWork(true);
                                logger.Info("force suspend");
                                Win32.SetSystemPowerState(null, Win32.POWER_STATE_SUSPEND, Win32.POWER_FORCE);
                                break;
                            }
                            OnPowerNotify(new PowerNotifyEventArgs(pB.PI.bACLineStatus, pB.PI.bBatteryLifePercent));
                        }
                        break;
                }
            }
        }

        /// <summary>
        /// Disable Network
        /// </summary>
        /// <param name="disableHardware">
        /// if true :disable hardware
        /// </param>
        private void StopNetWork(bool disableHardware)
        {
            Device.Com2PowerOff();
            Thread.Sleep(300);

            while (NetworkManager.IsConnecting)
            {
                Thread.Sleep(500);
            }

            switch (NetworkManager.ConnectType)
            {
                case ConnType.Gprs:
                    {
                        Device.DisConnectGprs(NetworkManager.GprsName);
                        if (disableHardware)
                        {
                            Device.DisableGsmModule();
                        }

                    }
                    break;
                case ConnType.Wlan:
                    {
                        if (disableHardware)
                        {
                            Device.DisableWlanModule();
                        }
                    }
                    break;
            }
        }
        #endregion
    }
}
